using System;
using DecisionMaking.Domain;
using NHibernate.Exceptions;
using System.Security.Cryptography;
using System.Text;
using DecisionMaking.BusinessLogic.Model;
using NHibernate;
using Castle.Services.Transaction;
using Castle.ActiveRecord;

namespace DecisionMaking.BusinessLogic.Impl
{
	[Transactional]
	public class AccountManager : IAccountManager
	{
		public Account Register(RegisterModel model)
		{
			try 
			{
				Account account = new Account
				{
					Email = model.Email,
					FirstName = model.FirstName,
					LastName = model.LastName,
					Password = Hash(model.Password)
				};
				
				account.SaveAndFlush();
				
				return account;
			}
			catch (ConstraintViolationException e) 
			{
				if (e.ConstraintName == "uix_account_email") 
				{
					throw new DuplicateEmailException(model.Email, e);
				}
				else 
				{
					throw; // unexpected exception, throw to caller
				}
			}
		}
		
		public Account UpdateAccount(UpdateAccountModel model)
		{
			try
			{
				Account account = GetAccount(model.Id);
				
				account.FirstName = model.FirstName;
				account.LastName = model.LastName;
				account.Email = model.Email;
				account.Bio = model.Bio;
				
				account.SaveAndFlush();
				
				return account;
			}
			catch (ConstraintViolationException e) 
			{
				if (e.ConstraintName == "uix_account_email") 
				{
					throw new DuplicateEmailException(model.Email, e);
				}
				else 
				{
					throw; // unexpected exception, throw to caller
				}
			}			
		}
		
		[Transaction(Castle.Services.Transaction.TransactionMode.NotSupported)]
		public Account GetAccount(int id)
		{
			try
			{
				return Account.Find(id);
			}
			catch (ObjectNotFoundException e)
			{
				throw new EntityNotFoundException(typeof(Account), id, e);
			}
		}
		
		[Transaction(Castle.Services.Transaction.TransactionMode.NotSupported)]
		public Account GetAccountByCredentials(string email, string password)
		{
			Account account = Account.FindByEmail(email);
			
			if (account == null || !Hash(password).Equals(account.Password))
			{
				throw new AccountNotFoundException(email);
			}
			else
			{
				return account;
			}
		}
		
		/// <summary>
		/// Returns a hex-encoded hash of the input string 
		/// </summary>
		/// <param name="input">
		/// A <see cref="System.String"/>
		/// </param>
		/// <returns>
		/// A <see cref="String"/>
		/// </returns>
		private String Hash(string input)
		{
			MD5 md5Hasher = MD5.Create();
			
	        byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));	
	        StringBuilder sBuilder = new StringBuilder();
	        for (int i = 0; i < data.Length; i++)
	        {
	            sBuilder.Append(data[i].ToString("x2"));
	        }

	        return sBuilder.ToString();
		}
		
	
	}
}

